package com.canal.display.config;

import com.canal.display.handler.LoginSuccessHandler;
import com.canal.display.handler.LogoutClearTokenHandler;
import com.canal.display.handler.TokenRefreshSuccessHandler;
import com.canal.display.service.TokenBasedAuthenticationProvider;
import com.canal.display.service.UserTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.client.RestTemplate;

/**
 * @author Administrator
 * @description
 * @date 2019年03月17 10:50
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public RedisTemplate redisTemplate() {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(factory);
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setValueSerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(redisSerializer);
        redisTemplate.setHashValueSerializer(redisSerializer);
        return redisTemplate;
    }


    @Bean
    public UserTokenService userLoginService() {
        return new UserTokenService(redisTemplate());
    }

    @Bean
    public LoginSuccessHandler loginSuccessHandler() {
        return new LoginSuccessHandler(userLoginService());
    }

    @Bean
    public TokenRefreshSuccessHandler tokenRefreshSuccessHandler() {
        return new TokenRefreshSuccessHandler(userLoginService());
    }

    @Bean
    public LogoutClearTokenHandler logoutClearTokenHandler() {
        return new LogoutClearTokenHandler(userLoginService());
    }

    /**
     * 注册provider
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider())
                .authenticationProvider(tokenBasedAuthenticationProvider());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean("tokenBasedAuthenticationProvider")
    protected TokenBasedAuthenticationProvider tokenBasedAuthenticationProvider() {
        return new TokenBasedAuthenticationProvider(userLoginService());
    }

    @Bean("daoAuthenticationProvider")
    protected AuthenticationProvider daoAuthenticationProvider() throws Exception {
        //这里会默认使用BCryptPasswordEncoder比对加密后的密码，注意要跟createUser时保持一致
        DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
        daoProvider.setPasswordEncoder(passwordEncoder());
        daoProvider.setUserDetailsService(userLoginService());
        return daoProvider;
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //静态资源不进入过滤器
                .authorizeRequests()
                //注册接口直接放过
                .antMatchers("/","/signup","/actuator/**","/js/**","/css/**","/fonts/**","/img/**","/index").permitAll().anyRequest().authenticated()
                .and()
                .csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                //增加登录过滤器配置
                .apply(new JsonLoginConfigurer<>()).loginSuccessHandler(loginSuccessHandler())
                .and()
                //增加token过滤器配置
                .apply(new TokenBasedConfigurer<>())
                .tokenBasedSuccessHandler(tokenRefreshSuccessHandler())
                .permissiveRequestUrls("/","/login","/signup","/js/**","/css/**","/fonts/**","/img/**","/index")
                .and()
                .logout()
                .addLogoutHandler(logoutClearTokenHandler())
                .and()
                .cors()
                .and()
                .sessionManagement().disable()
                ;
    }

}
